﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Timers;

namespace MyTestProject.NUnity.TcpClient
{
    /// <summary>
    /// Tcp客户实现
    /// </summary>
    public class TcpClientDo : ICommunication
    {
        private const int MAX_RECONNECT_TIMES = 3;  //断线重连尝试次数
        private const int COMM_IDLE_TIMES = 3;      //通讯空闲触发间隔

        //soket对象及参数
        private Socket _socket;
        private string _host;
        private int _port;
        private bool _reconnect;
        private int ConnecteFailedCount { get; set; }
        public int ReconnectStatistics { get; private set; }

        private static uint _keepAliveTime = 5000;      //无数据交互持续时间(ms)
        private static uint _keepAliveInterval = 500;   //发送探测包间隔(ms)

        //定时器,用于触发通讯空闲
        private Timer _timer;
        private int _commIdleCount;

        //实现接口的两个事件
        public event CommRxEventHandler CommRxEvent;
        public event CommIdleEventHandler CommIdleEvent;

        //重连失败事件
        public event EventHandler ReconnectionFailedEvent;

        //数据接收缓存
        private CommBuffer recvBuffer;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="host"></param>
        /// <param name="port"></param>
        public TcpClientDo(string host, int port)
        {
            _host = host;
            _port = port;
            _reconnect = false;
            ConnecteFailedCount = 0;
            recvBuffer = new CommBuffer(20480);
        }

        /// <summary>
        /// 连接
        /// </summary>
        public void Connect()
        {
            try
            {
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
                               ProtocolType.Tcp);
                _socket.Connect(_host, _port);
                ConnecteFailedCount = MAX_RECONNECT_TIMES;

                //设置KeepAlive
                _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                byte[] optionValue = new byte[12];
                BitConverter.GetBytes(1).CopyTo(optionValue, 0);
                BitConverter.GetBytes(_keepAliveTime).CopyTo(optionValue, 4);
                BitConverter.GetBytes(_keepAliveInterval).CopyTo(optionValue, 8);
                _socket.IOControl(IOControlCode.KeepAliveValues, optionValue, null);
            }
            catch (Exception ex)
            {
                Logger.WriteLog1("TCPConnectState",string.Format("{0}\r\n{1}",ex.Message,ex.StackTrace));
            }
        }

        /// <summary>
        /// 重连
        /// </summary>
        /// <returns></returns>
        public bool Reconnect()
        {
            ReconnectStatistics++;
            _reconnect = false;
            Close();
            try
            {
                Connect();
            }
            catch (SocketException e)
            {
                ConnecteFailedCount--;
                if (ConnecteFailedCount > 0)
                {
                    //Console.WriteLine("重试次数剩余{0}",ConnecteFailedCount);
                    _reconnect = true;
                    return true;
                }
                else
                {
                    //重连失败事件
                    if (ReconnectionFailedEvent != null)
                        ReconnectionFailedEvent(this, new EventArgs());
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Close()
        {
            _socket.Close();
        }

        /// <summary>
        /// 启动空闲事件触发
        /// </summary>
        public void StartCommIdle()
        {
            //定时器配置
            _timer = new System.Timers.Timer(500);
            _timer.AutoReset = true;
            _timer.Elapsed += TimerElapsed;
            _timer.Start();
            _commIdleCount = 0;
        }

        /// <summary>
        /// 停止空闲事件触发
        /// </summary>
        public void StopCommIdle()
        {
            _timer.Elapsed -= TimerElapsed;
            _timer.Close();
        }

        /// <summary>
        /// 发送接收数据事件
        /// </summary>
        public void SendRecvEvent()
        {
            if (CommRxEvent != null)
                CommRxEvent();
        }

        /// <summary>
        /// 发送超时事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            if (_commIdleCount++ >= COMM_IDLE_TIMES)
            {
                if (CommIdleEvent != null)
                    CommIdleEvent();
                _commIdleCount = 0;
            }
        }

        /// <summary>
        /// 发送数据接收实现,断线重连
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public int CommSend(byte[] buffer, int size)
        {
            int sendSize = 0;
            try
            {
                sendSize = _socket.Send(buffer, size, SocketFlags.None);
                _commIdleCount = 0;
            }
            catch (SocketException e)
            {
                ReconnectStatistics++;
                _reconnect = true;
            }
            return sendSize;
        }

        /// <summary>
        /// 接收数据接口实现
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public int CommReceive(byte[] buffer, int size)
        {
            return recvBuffer.Read(buffer, size);
        }

        /// <summary>
        /// 接收数据接口实现
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public int CommReceive(CommBuffer buffer)
        {
            return recvBuffer.CopyTo(buffer);
        }

        /// <summary>
        /// 接收数据线程,使用阻塞方式接收数据
        /// </summary>
        public void ReadFormSocket()
        {
            int recvNum;
            byte[] recv = new byte[2048];
            while (true)
            {
                try
                {
                    recvNum = _socket.Receive(recv, SocketFlags.None);
                }
                catch (SocketException e)
                {
                    recvNum = 0;
                }

                //网络断开Receive返回0
                if (recvNum == 0 || _reconnect == true)
                {
                    //重连次数用尽,退出
                    if (Reconnect() == false)
                        break;
                }
                else
                {
                    recvBuffer.Write(recv, recvNum);
                    SendRecvEvent();
                }
                _commIdleCount = 0;
            }
        }
    }
}